﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using AddIn.EA.Framework;
using AddIn.EA.Architecture.ComponentLogic.ElementModel;

namespace AddIn.EA.Architecture.Presentation
{
    public partial class CreateStandardProjectMethods : BaseAddInArchitecture
    {
        #region Local Objects
        private ComponentLogic.StandardProject.MethodStandardProject methodEdit;
        private List<ComponentLogic.StandardProject.MethodStandardProject> methods;
        private ComponentLogic.StandardProject.BusinessEntityStandardProject baseEntity;
        private List<ComponentLogic.StandardProject.BusinessEntityStandardProject> entities;
        #endregion

        #region Starting Form
        public CreateStandardProjectMethods(List<ComponentLogic.StandardProject.MethodStandardProject> methods,
                                            ComponentLogic.StandardProject.BusinessEntityStandardProject entity,
                                            RepositoryManager repository,
                                            List<ComponentLogic.StandardProject.BusinessEntityStandardProject> entities)
        {
            InitializeComponent();
            this.Repository = repository;
            base.LoadAddInControls += new EventHandler(CreateStandardProjectMethods_LoadAddInControls);
            this.methods = new List<AddIn.EA.Architecture.ComponentLogic.StandardProject.MethodStandardProject>(methods);
            this.baseEntity = entity;
            this.entities = entities;
        }
        public CreateStandardProjectMethods(List<ComponentLogic.StandardProject.MethodStandardProject> methods,
                                            RepositoryManager repository,
                                            List<ComponentLogic.StandardProject.BusinessEntityStandardProject> entities)
            : this(methods, null, repository, entities)
        {

        }
        private void CreateStandardProjectMethods_Load(object sender, EventArgs e)
        {
            StartForm();
        }
        void CreateStandardProjectMethods_LoadAddInControls(object sender, EventArgs e)
        {
            StartForm();
        }
        private void StartForm()
        {
            try
            {
                LoadBaseEntityLayers();
                LoadScope();
                LoadMethods();
                CheckExistingMethods();
                CreateStandardCRUDMethods();
            }
            catch (Exception ex)
            {
                HandleException("Inicialização da tela.", ex);
            }
        }
        private void LoadBaseEntityLayers()
        {
            if (baseEntity != null)
                ucLayers.SetLayersBuilders(baseEntity.Builders, true);
            else
                ucLayers.SetAllChecks(false);
        }
        #endregion

        #region Events
        private void cmdSave_Click(object sender, EventArgs e)
        {
            SaveMethod();
        }
        private void cmdCelan_Click(object sender, EventArgs e)
        {
            CleanMethodForm();
        }
        private void cmdEdit_Click(object sender, EventArgs e)
        {
            EditSelectedMethod();
        }
        private void cmdRemove_Click(object sender, EventArgs e)
        {
            RemoveSelectedMethod();
        }
        private void lstMethods_DoubleClick(object sender, EventArgs e)
        {
            EditSelectedMethod();
        }
        private void lstMethods_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete)
                RemoveSelectedMethod();
        }
        private void ctxEditMethod_Click(object sender, EventArgs e)
        {
            EditSelectedMethod();
        }
        private void ctxRemoveMethod_Click(object sender, EventArgs e)
        {
            RemoveSelectedMethod();
        }
        private void cmdCleanAllLayers_Click(object sender, EventArgs e)
        {
            CleanMethodFormAllLayers();
        }
        private void cmdOK_Click(object sender, EventArgs e)
        {
            ConfirmSelection();
        }
        private void ckMethodsAll_CheckedChanged(object sender, EventArgs e)
        {
            SetMethodsSelection(ckMethodsAll.Checked);
        }
        private void txtName_KeyDown(object sender, KeyEventArgs e)
        {
            ValidateElementNameWriting(e);
        }
        private void txtParameters_KeyDown(object sender, KeyEventArgs e)
        {
            ValidateElementNameWriting(e);
        }
        private void txtReturn_KeyDown(object sender, KeyEventArgs e)
        {
            ValidateElementNameWriting(e);
        }
        private void cmdImportMethods_Click(object sender, EventArgs e)
        {
            ImportMethods();
        }
        #endregion

        #region Methods
        private void LoadScope()
        {
            try
            {
                String[] list = Enum.GetNames(typeof(ElementScope));
                foreach (String item in list)
                    cmbScope.Items.Add(item);
                cmbScope.SelectedIndex = cmbScope.Items.Count > 0 ? 0 : -1;
            }
            catch (Exception ex)
            {
                HandleException("Carregar as listas de escopos.", ex);
            }
        }
        private void CreateStandardCRUDMethods()
        {
            if (methods == null)
                methods = new List<AddIn.EA.Architecture.ComponentLogic.StandardProject.MethodStandardProject>();

            Framework.Element crudElement = ComponentLogic.StandardProject.StandardProject.TemplateRepository.GetPackageByName("CRUD").GetElementByName("CRUD");
            foreach (Framework.Method itemMethod in crudElement.Methods)
            {
                String methodName = itemMethod.Name.Replace("[entity_name]", baseEntity.Name).Replace("[entity_plural_name]", baseEntity.NamePlural);

                if (!methods.Exists(it => it.Name.CompareTo(methodName) == 0))
                {
                    ComponentLogic.StandardProject.MethodStandardProject newMethod = new ComponentLogic.StandardProject.MethodStandardProject
                    {
                        Builders = ucLayers.GetLayerBuilders(),
                        IsStatic = itemMethod.IsStatic,
                        Name = methodName,
                        Notes = itemMethod.Notes.Replace("[entity_name]", baseEntity.Name),
                        Parameters = GetMethodParameterDescription(itemMethod.Parameters),
                        Return = itemMethod.ReturnType.Replace("entity_name", baseEntity.Name).Replace("entity_plural_name", baseEntity.NamePlural),
                        Scope = (Scope)Enum.Parse(typeof(Scope), itemMethod.Scope.ToString())
                    };
                    newMethod.Sequence = GetMethodNextSequence();
                    methods.Add(newMethod);
                }
            }
            LoadMethods();
        }
        private int GetMethodNextSequence()
        {
            return methods.Count > 0 ? methods.Max(new Func<ComponentLogic.StandardProject.MethodStandardProject, int>(GetMethodMaxSelector)) + 1 : 1;
        }
        private int GetMethodMaxSelector(ComponentLogic.StandardProject.MethodStandardProject itemMethod)
        {
            return itemMethod.Sequence;
        }
        private string GetMethodParameterDescription(ParameterList parameterList)
        {
            String result = String.Empty;

            foreach (Parameter item in parameterList)
                result += item.Name.Replace("param_name_plural", baseEntity.NamePlural.ToLowerInvariant()).Replace("param_name", baseEntity.Name.ToLowerInvariant())
                       + ":" + item.Type.Replace("entity_name", baseEntity.Name).Replace("entity_plural_name", baseEntity.NamePlural) + ";";

            if (!String.IsNullOrEmpty(result))
                result = result.Remove(result.Length - 1, 1);

            return result;
        }
        private void LoadMethods()
        {
            try
            {
                List<String> checkedItems = new List<string>();
                foreach (ListViewItem listItem in lstMethods.CheckedItems)
                    checkedItems.Add(listItem.SubItems[0].Text + "|" + listItem.SubItems[1].Text);

                lstMethods.Items.Clear();
                if (methods != null)
                {
                    foreach (ComponentLogic.StandardProject.MethodStandardProject item in methods)
                    {
                        ListViewItem listItem = new ListViewItem(item.Name);
                        listItem.SubItems.Add(item.Parameters);
                        listItem.SubItems.Add(item.Return);
                        listItem.SubItems.Add(item.Sequence.ToString());
                        lstMethods.Items.Add(listItem);
                    }
                }
                lstMethods.Sort();

                foreach (ListViewItem listItem in lstMethods.Items)
                {
                    if (checkedItems.Exists(it => listItem.SubItems[0].Text.CompareTo(it.Split('|')[0].Trim()) == 0 &&
                                                  listItem.SubItems[1].Text.CompareTo(it.Split('|')[1].Trim()) == 0))
                        listItem.Checked = true;
                }
            }
            catch (Exception ex)
            {
                HandleException("Carregar a lista de métodos.", ex);
            }
        }
        private void CheckExistingMethods()
        {
            foreach (ListViewItem listItem in lstMethods.Items)
            {
                if (methods.Exists(it => listItem.SubItems[3].Text.CompareTo(it.Sequence.ToString()) == 0))
                    listItem.Checked = true;
            }
        }
        private void SaveMethod()
        {
            try
            {
                if (ValidateMethodForm())
                {
                    ComponentLogic.StandardProject.MethodStandardProject method = GetMethodData();
                    Int32 idx = methods.FindIndex(it => it.Sequence.CompareTo(method.Sequence) == 0);
                    if (idx != -1)
                        methods[idx] = method;
                    else
                        methods.Add(method);

                    CleanMethodForm();
                    LoadMethods();
                }
            }
            catch (Exception ex)
            {
                HandleException("Salvar o método.", ex);
            }
        }
        private void CleanMethodForm()
        {
            try
            {
                txtName.Text = String.Empty;
                txtNotes.Text = String.Empty;
                txtParameters.Text = String.Empty;
                txtReturn.Text = String.Empty;
                ckIsAbstract.Checked = false;
                ckIsStatic.Checked = false;
                cmbScope.SelectedIndex = cmbScope.Items.Count > 0 ? 0 : -1;
                txtName.Focus();
                LoadBaseEntityLayers();
                methodEdit = null;
            }
            catch (Exception ex)
            {
                HandleException("Limpar o formulário de edição do método.", ex);
            }
        }
        private AddIn.EA.Architecture.ComponentLogic.StandardProject.MethodStandardProject GetMethodData()
        {
            ComponentLogic.StandardProject.MethodStandardProject method = new AddIn.EA.Architecture.ComponentLogic.StandardProject.MethodStandardProject
            {
                IsAbstract = ckIsAbstract.Checked,
                IsStatic = ckIsStatic.Checked,
                Name = txtName.Text.Trim(),
                Notes = txtNotes.Text.Trim(),
                Parameters = txtParameters.Text.Trim(),
                Return = txtReturn.Text.Trim(),
                Scope = (Scope)Enum.Parse(typeof(Scope), cmbScope.SelectedItem.ToString()),
                Builders = ucLayers.GetLayerBuilders()
            };
            if (methodEdit != null)
                method.Sequence = methodEdit.Sequence;
            return method;
        }
        private bool ValidateMethodForm()
        {
            bool valid = true;
            String message = "Existem erros de validação no método. Verifique as mensagens abaixo:\r\n";

            if (String.IsNullOrEmpty(txtName.Text.Trim()))
            {
                valid = false;
                message += "Informe o nome do método.";
            }
            if (!String.IsNullOrEmpty(txtParameters.Text.Trim()))
            {
                Regex rgx = new Regex(@"^[a-zA-Z_]+[a-zA-Z_\.]*[a-zA-Z_]+:[a-zA-Z_]+[a-zA-Z_\.<>]*[a-zA-Z_>]+(;[a-zA-Z_]+[a-zA-Z_\.]*[a-zA-Z_]+:[a-zA-Z_]+[a-zA-Z_\.<>]*[a-zA-Z_>]+)*$");
                if (!rgx.IsMatch(txtParameters.Text.Trim()))
                {
                    valid = false;
                    message += "Os parâmetros do método estão em um formato incorreto. Formato <nome>:<tipo> (Separados por \";\")";
                }
            }
            if (String.IsNullOrEmpty(txtReturn.Text.Trim()))
            {
                valid = false;
                message += "Informe o retorno do método.";
            }
            if (!ucLayers.IsAnyLayerChecked)
            {
                valid = false;
                message += "\r\nSelecione ao menos uma camada para o método.";
            }
            if (methodEdit == null &&
                methods != null &&
                methods.Count > 0 &&
                methods.Exists(it => it.Name.CompareTo(txtName.Text.Trim()) == 0 && it.Parameters.CompareTo(txtParameters.Text.Trim()) == 0))
            {
                valid = false;
                message += "\r\nO método informado já existe!";
            }

            if (!valid)
                MessageBox.Show(message, "Alerta", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

            return valid;
        }
        private void EditSelectedMethod()
        {
            try
            {
                if (lstMethods.SelectedItems.Count == 1)
                {
                    methodEdit = methods.Find(it => it.Sequence.ToString().CompareTo(lstMethods.SelectedItems[0].SubItems[3].Text) == 0);
                    if (methodEdit != null)
                    {
                        txtName.Text = methodEdit.Name;
                        txtNotes.Text = methodEdit.Notes;
                        txtParameters.Text = methodEdit.Parameters;
                        txtReturn.Text = methodEdit.Return;
                        cmbScope.SelectedItem = methodEdit.Scope.ToString();
                        ckIsAbstract.Checked = methodEdit.IsAbstract;
                        ckIsStatic.Checked = methodEdit.IsStatic;
                        ucLayers.SetLayersBuilders(methodEdit.Builders);
                    }
                }
                else
                    MessageBox.Show("Selecione um método para editar!", "Alerta", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
            catch (Exception ex)
            {
                HandleException("Iniciar edição do método selecionado.", ex);
            }
        }
        private void RemoveSelectedMethod()
        {
            try
            {
                if (lstMethods.SelectedItems.Count > 0)
                {
                    if (MessageBox.Show("Deseja realmente remover os métodos selecionados?", "Questão", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                    {
                        while (lstMethods.SelectedItems.Count > 0)
                        {
                            methods.RemoveAll(it => it.Sequence.ToString().CompareTo(lstMethods.SelectedItems[0].SubItems[3].Text) == 0);
                            lstMethods.SelectedItems[0].Remove();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                HandleException("Remover o método selecionado.", ex);
            }
        }
        private void CleanMethodFormAllLayers()
        {
            ucLayers.SetAllChecks(false);
        }
        private void ConfirmSelection()
        {
            List<ComponentLogic.StandardProject.MethodStandardProject> returnMethods = new List<AddIn.EA.Architecture.ComponentLogic.StandardProject.MethodStandardProject>();
            foreach (ListViewItem listItem in lstMethods.Items)
            {
                if (listItem.Checked)
                    returnMethods.Add(methods.Find(it => it.Sequence.ToString().CompareTo(listItem.SubItems[3].Text) == 0));
            }
            this.Tag = returnMethods;
            this.Close();
        }
        private void SetMethodsSelection(bool state)
        {
            foreach (ListViewItem listItem in lstMethods.Items)
                listItem.Checked = state;
        }
        private void ValidateElementNameWriting(KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Space)
                e.SuppressKeyPress = true;
        }
        private void ImportMethods()
        {
            try
            {
                CreateStandardProjectImportEntityMethods wd = new CreateStandardProjectImportEntityMethods(entities);
                wd.ShowDialog(this);
                if (wd.Tag != null && wd.Tag is List<ComponentLogic.StandardProject.MethodStandardProject>)
                {
                    List<ComponentLogic.StandardProject.MethodStandardProject> importedMethods = wd.Tag as List<ComponentLogic.StandardProject.MethodStandardProject>;
                    if (importedMethods.Count > 0)
                    {
                        List<ComponentLogic.StandardProject.MethodStandardProject> result = importedMethods.FindAll(it => !methods.Exists(itm => itm.Name.CompareTo(it.Name) == 0 && itm.Parameters.CompareTo(it.Parameters) == 0));
                        if (result != null && result.Count > 0)
                        {
                            result.ForEach(it => it.Sequence = GetMethodNextSequence());
                            methods.AddRange(result);
                            LoadMethods();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                HandleException("Importar métodos de outra classe", ex);
            }
        }
        #endregion
    }
}
